package lh2;

import java.util.ArrayList;
import java.util.List;
import java.io.*;
import java.util.StringTokenizer;
import java.net.*;
import java.io.*;
import com.thoughtworks.xstream.XStream;
import java.util.Hashtable;
import java.util.Random;
import lh2.StringEncrypter.EncryptionException;



public class Cliente {

    private static int primo=7;
    private static int base=3;
    static Hashtable<Integer, Integer> servidoresConocidos=new Hashtable<Integer, Integer>();
    private static NumeroSecreto numerosecreto;
    private static int numero_privado;
    static int l;
    static int s;



    private static void enviar(Mensaje mensaje, Socket conexion)
    {
        try
        {
          OutputStreamWriter osw = new OutputStreamWriter(new BufferedOutputStream(conexion.getOutputStream()), "US-ASCII");
          XStream xstream = new XStream();
          xstream.alias("mensaje", Mensaje.class);
          String msj_marsh_xml = xstream.toXML(mensaje)+  (char) 13;
          osw.write(msj_marsh_xml);
          osw.flush();
          desconectarse(conexion);
        }
        catch (IOException f) {
          System.out.println("IOException: " + f);
        }
        catch (Exception g) {
          System.out.println("Exception: " + g);
        }

    }

    private static Socket conectarAServidor(int puerto)
    {
        try
        {
            String host = "localhost";
            InetAddress address = InetAddress.getByName(host);
            Socket conexion = new Socket(address, puerto);
            return conexion;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    private static Mensaje recibir(Socket conexion)
    {
        StringBuffer instr = new StringBuffer();
        try
        {
            BufferedInputStream bis = new BufferedInputStream(conexion.getInputStream());
            InputStreamReader isr = new InputStreamReader(bis, "US-ASCII");
            int c;
            while ( (c = isr.read()) != 13)
                instr.append( (char) c);
            System.out.println(instr);
            String xml=instr.toString();

            //Aca hacemos el desencriptado de los resultados
            XStream xstream2 = new XStream();
            xstream2.alias("mensaje", Mensaje.class);
            Mensaje mensaje = (Mensaje)xstream2.fromXML(xml);
            System.out.println(mensaje.getContenido().toString());
            //si esta encriptado hay que desencriptar
            System.out.println("esta cifrado? "+mensaje.getEstaCifrado());
            if (mensaje.getEstaCifrado()){
                String contenidoenxml=desencriptar(Cliente.numerosecreto.getClaveSecreta(),(String)mensaje.getContenido());
                XStream xstreamregistros = new XStream();
                xstreamregistros.alias("registros", ArrayList.class);
                List<Registro> registros = (ArrayList<Registro>)xstream2.fromXML(contenidoenxml);
                System.out.println("mensaje Unmarshaleado YEEE: "+registros.get(0).campo);
                //actualizamos los registros desencriptados:
                mensaje.setContenido(registros);
            }
            
            desconectarse(conexion);
            return mensaje;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    private static void desconectarse(Socket conexion)
    {
        try{conexion.close();}catch(Exception e){e.printStackTrace();}
    }

    private static Demanda pedirComando()
    {
        try
        {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1);
            boolean salir=false;
            String entrada;

            Demanda dem=null;

            while(dem==null)
            {
                System.out.print("o.O': ");
                entrada=br.readLine();
                StringTokenizer t=new StringTokenizer(entrada, " ");
                String t1=t.nextToken();
                String t2="";
                String t3="";
                if(t1.equals(Demanda.RECUPERAR) && t.countTokens()==1)
                {
                    t2=t.nextToken();
                    dem=new Demanda(Demanda.RECUPERAR, t2);
                }
                else if(t1.equals(Demanda.BUSCAR) && t.countTokens()==1)
                {
                    System.out.println("tengo 1 tokens");
                    t2=t.nextToken();
                    //dem=new Demanda(Demanda.BUSCAR, Integer.parseInt(t2));
                    dem=new Demanda(Demanda.BUSCAR, t2);
                }
                else if(t1.equals(Demanda.INSERTAR) && t.countTokens()==2)
                {
                    t2=t.nextToken();
                    t3=t.nextToken();
                    int clave=Integer.parseInt(t2);
                    dem=new Demanda(Demanda.INSERTAR, Integer.parseInt(t2), t3);
                }
                else if(t1.equals(Demanda.DIFFIEHELMAN) && t.countTokens()==0)
                {
                    
                    Random rnd = new Random();
                    int numeroprivado= (int)Math.abs(rnd.nextInt()%10);//achicamos el numero aleatorio entre 0 y 10
                    Cliente.numero_privado =numeroprivado;
                    int gpowamodp=(int)(Math.pow(Cliente.base, Cliente.numero_privado) % Cliente.primo);
                    System.out.println("numero privado del cliente="+Cliente.numero_privado+", cliente manda al server "+gpowamodp);
                    dem=new Demanda(Demanda.DIFFIEHELMAN,gpowamodp);
                }
                else if(t1.equals(Demanda.ELIMINAR) && t.countTokens()==1)
                {
                    t2=t.nextToken();
                    dem=new Demanda(Demanda.ELIMINAR, Integer.parseInt(t2));
                }
                else if(t1.equals("salir")) {
                    dem=new Demanda("salir", null);
                }
                else
                {
                    System.out.println("No se reconoce el comando");
                }
            }
            return dem;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return  null;
        }
    }

	public static void main(String[] args) throws Exception {
            Demanda dem=pedirComando();
            while(!dem.getOperacion().equals("salir")){
                Socket conexion=conectarAServidor(lhStar(dem.getClave()));
                int origen=conexion.getLocalPort();
                Mensaje mensaje;
                if (dem.getOperacion().toString().equals(Demanda.DIFFIEHELMAN.toString())){
                    mensaje =new Mensaje(origen,conexion.getPort(), dem,false);//si es un mensaje de intercambio de claves no viaja cifrado
                }else{
                    //aca hace las magias del cifrado

                    XStream xstream = new XStream();
                    xstream.alias("demanda", Demanda.class);
                    String demandaxml = xstream.toXML(dem);
                    System.out.println(demandaxml);
                    System.out.println(Cliente.numerosecreto.getClaveSecreta());
                    String demandaencriptada=encriptar(Cliente.numerosecreto.getClaveSecreta(),demandaxml);
                    System.out.println("la demanda cliente encriptada: "+demandaencriptada);
                    mensaje =new Mensaje(origen,conexion.getPort(), demandaencriptada);
                }

                enviar(mensaje, conexion);
                //desconectarse(conexion);
                System.out.println(origen);
                ServerSocket puertoEscucha=new ServerSocket(origen);
                Socket socketRespuesta=puertoEscucha.accept();
                Mensaje respuesta=recibir(socketRespuesta);
                List<Registro> listaderegistros=(ArrayList<Registro>)respuesta.getContenido();
                Registro reg=listaderegistros.get(0);
                System.out.println("si anda me volvi loco: "+reg.campo+", clave: "+reg.clave);
                //(int numero,int idcontraparte,int valorcontraparte)
                int respuestacontraparte=reg.clave;
                int clavesecreta=(int)(Math.pow(respuestacontraparte, Cliente.numero_privado)%7);
                Cliente.numerosecreto=new NumeroSecreto(clavesecreta,respuesta.getOrigen());
                System.out.println("LLAVE EN CLIENTE: "+Cliente.numerosecreto.getClaveSecreta()+", contraparte: "+Cliente.numerosecreto.getIdContraparte());
                dem=pedirComando();
            }
	}

        public static String encriptar(String clave, String aencriptar) throws EncryptionException{

            String cadenaAEncriptar = aencriptar;
            String esquema = StringEncrypter.DESEDE_ENCRYPTION_SCHEME;
            StringEncrypter encriptador = new StringEncrypter( esquema, clave);
            String cadenaEncriptada = encriptador.encrypt( cadenaAEncriptar );
            System.out.println("test encriptado: "+cadenaEncriptada);
            return cadenaEncriptada;
        }
        public static String desencriptar(String clave, String adesencriptar) throws EncryptionException{

            //desencriptado
            String llaved = clave;
            String esquemad = StringEncrypter.DESEDE_ENCRYPTION_SCHEME;
            StringEncrypter encriptador = new StringEncrypter(esquemad,llaved);
            String desencriptado = encriptador.decrypt(adesencriptar);
            System.out.println("texto desencriptado: "+desencriptado);
            return desencriptado;
        }

        
        private static void escuchar(ServerSocket puerto) {
              try {
                  while (true) {
                    Socket conexion = puerto.accept();
                    Mensaje mensaje=recibir(conexion);

                    XStream xstream2 = new XStream();
                    xstream2.alias("demanda", Demanda.class);

                    Mensaje respuesta;
                    //Hay que descifrar la demanda
                    String demandaencriptada=(String)mensaje.getContenido();
                    System.out.println("CLIENTE ESCUCHANDO : "+demandaencriptada);
                    String demandadesencriptadaxml=desencriptar(Cliente.numerosecreto.getClaveSecreta(),demandaencriptada);
                    XStream xstream = new XStream();
                    xstream.alias("demanda", Demanda.class);
                    Demanda dem = (Demanda)xstream2.fromXML(demandadesencriptadaxml);
                    System.out.println("MENSAJE DESENCRIPTADO:\n "+dem.getCadena());
                    //System.out.println(servidoresConocidos.toString());

   
                    
                  }
              }catch(Exception e) {
                  e.printStackTrace();
              }
           }

            private static void ejecutarDemanda(Demanda dem){
                if(dem.getOperacion().equals(Demanda.ACTUALIZAR)){
                    XStream xstream = new XStream();
                    xstream.alias("actualizacion",Actualizacion.class);
                    Actualizacion act=(Actualizacion)xstream.fromXML(dem.getCadena());
                    servidoresConocidos=act.servidores;
                    s=act.s;
                    l=act.l;
                }
            

            }


	public static int lhStar(int clave){
	    //return this.servidoresConocidos.get(0).getPuerto();
            return 199;
	}

}
